﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

// Reviewed
#pragma once

#include "GcmpModelInterface.h"
#include "IObject.h"
#include "WeakPtr.h"
#include "DocumentLoadError.h"
#include "IDocumentOpenValidator.h"
#include "GcmpApiDeclaration.h"
#include "DocumentLoadOptions.h"

namespace gcmp
{
    /// \brief 文档类型。
    enum class DocumentType : int32_t
    {
        FamilyDocument = 0,   ///< 族文档
        ProjectDocument = 1,  ///< 项目文档
    };

    class IDocument;
    class RuntimeId;
    class ElementId;
    class IModelDataFilesSelector;
    class IDocumentEvent;
    class IDocumentInitializer;
    class IFileExtensions;
    class ClassId;
    class IElementFilter;
    class ILinkDatasetElementFilter;

    /// \brief 文档管理器
    ///
    /// 文档管理器为单例类，负责对文档进行管理（创建、打开、关闭等）
    FINAL_INTERFACE class GCMP_MODEL_INTERFACE_EXPORT IDocumentManager : public IObject
    {
        DEFINE_CAST_DERIVED(gcmp::IDocumentManager, gcmp::IObject);
    public:
        /// \brief 析构函数
        virtual ~IDocumentManager() {};

        /// \brief 获取文档管理器
        /// \return 文档管理器指针
        static IDocumentManager* Get();

        /// \brief 根据RuntimeId获取文档
        /// \param documentId 文档的RuntimeId
        /// \return 文档指针
        virtual IDocument* GetDocument(const RuntimeId& documentId) = 0;

        /// \brief 根据RuntimeId关闭对应文档
        /// \param documentId 文档的RuntimeId，不保存文档
        /// \return 成功返回true，失败返回false
        virtual bool CloseDocument(const RuntimeId documentId) = 0;

        /// \brief 关闭当前打开的所有文档
        /// \return void
        virtual void CloseAllDocuments() = 0;

        /// \brief 打开文档
        /// \param filePath 要打开的文档绝对路径 
        /// \param docLoadOptions 文件加载标记选项
        /// \param loadResult 输出参数。打开文档失败时，返回错误码和错误消息。
        /// \param pDocOpenValidator 文档打开验证器，不用的话，可以设置为空
        /// \param pUnrecognizedAssemblies 文档打开未识别的模块名称，作为输入返回值，不用的话，可以不设置，默认为空
        /// \return 返回Document对象，如果失败则返回空。
        virtual IDocument* OpenDocument
        (
            const std::wstring& filePath,
            DocumentLoadOptions& docLoadOptions,
            DocumentLoadResult& loadResult,
            IDocumentOpenValidator* pDocumentOpenValidator,
            std::set<std::wstring>* pUnrecognizedAssemblies
        ) = 0;

        /// \brief 创建文档
        /// \param documentType 文档的类型，不同类型文档会有不同的设置
        /// \param documentName 文档名称
        /// \param pInitializer 文档初始化器，若为空则平台对文档不进行初始化
        /// \return 返回IDocument指针，如果失败则返回空
        virtual IDocument* CreateDocument(DocumentType documentType, const std::wstring& candidateName, IDocumentInitializer* pInitializer) = 0;

        /// \brief 获取打开文档的数量
        /// \return 打开文档的数量
        virtual size_t GetDocumentCount() const = 0;

        /// \brief 通过索引获取文档
        /// \return 返回IDocument指针，如果失败则返回空
        virtual const IDocument* GetDocumentByIndex(size_t index) const = 0;

        /// \brief 通过文件路径检查文件是否已经打开
        /// \param filePath 要检查的文件路径
        /// \return 返回true表示文件已经打开，false表示文件未打开
        virtual bool IsFileOpened(const std::wstring& filePath) const = 0;

        /// \brief 通过文件路径检查文件是否已经以被链接的方式打开
        /// \param filePath 要检查的文件路径
        /// \return 返回true表示文件已经以被链接的方式打开，false表示文件未以被链接的方式打开
        virtual bool IsFileLinked(const std::wstring& filePath) const = 0;

        /// \brief 获取文档事件
        /// 
        /// 文档事件会在文档创建、打开、关闭等情况发送
        /// \see DocumentEventType
        /// \return 返回文档事件指针
        virtual IDocumentEvent* GetDocumentEvent() const = 0;

        /// \brief 获取文件存储后缀名的实现类
        /// \return 返回文件存储后缀类的指针
        virtual const IFileExtensions* GetFileExtensions() const = 0;

        /// \brief 设置文件存储后缀名的实现类
        /// \param opFileExtensions 要设置的文件存储后缀名类的对象
        /// \return 返回true表示设置成功，false表示返回失败
        virtual bool SetFileExtensions(OwnerPtr<IFileExtensions> opFileExtensions) = 0;

        /// \brief 设置链接模型文件的元素过滤器
        /// 
        /// 用于链接模型文件时确定哪些Element被链接到主文档
        /// \param opElementFilter 元素过滤器对象
        /// \return 返回true表示设置成功，false表示返回失败
        virtual bool SetLinkDocumentElementFilter(OwnerPtr<IElementFilter> opElementFilter) = 0;

        /// \brief 获取当前链接模型文件的元素过滤器
        /// \return 元素过滤器
        virtual const IElementFilter* GetLinkDocumentElementFilter() = 0;

        /// \brief 获取关联更新数据是否即时传播
        /// \return true - 即时传播，false - 延时传播
        virtual bool InstantlyPropagatesRegenData() const = 0;

        /// \brief 设置关联更新数据是否即时传播
        /// 
        /// 默认是延时传播。如果没有特殊原因，不要打开即时传播，可能会影响性能和正确性。
        /// \param instantlyPropagate true - 即时传播，false - 延时传播
        virtual void SetInstantlyPropagatesRegenData(bool instantlyPropagate) = 0;

#pragma region 构件级协同原型
        /// \brief 创建初始化构件级协同项目用的模板文档
        ///
        /// 构件级协同项目初始化时，创建此文档，保存下来，上传文件，生成只包含部分只读的公共对象的公共数据集。
        ///
        /// \param pInitializer 文档初始化器，若为空则不调用。产品通过此初始化器添加自己的公共对象。
        /// \param datasetSubId 工作单元SubId，模板的默认值为1
        /// \return 返回IDocument指针，如果失败则返回空
        /// \note 目前未完全确定有哪些对象可以放入，发展中...
        virtual IDocument * CreateTemplateDocumentToInitializeElementCollaborationProject(IDocumentInitializer * pInitializer, UInt16 datasetSubId) = 0;
#pragma endregion

        /// \brief 收集文件中需要升级的classId
        ///
        /// \param filePath 文件路径
        /// \param[out] needUpgradeClassId 收集到文件中需要升级的classId
        /// \param[out] pErrorMessage 如果传入非空，失败时返回错误信息
        /// \return bool 成功返回true;失败false
        static bool CollectNeedUpgradeClassIdOfFile(
            const std::wstring& filePath,
            std::set<ClassId>& needUpgradeClassId,
            std::wstring* pErrorMessage = nullptr
        );

        /// \brief 设置文档打开过程中是否被中断，比如文档打开过程中关闭app
        ///
        /// \param isDocumentOpeningInterrupted 文档在打开过程中是否被中断
        /// \return void
        virtual void SetIsDocumentOpeningInterrupted(bool isDocumentOpeningInterrupted) = 0;

        /// \brief 判断文档打开过程中是否被中断，比如文档打开过程中关闭app
        ///
        /// \return bool true:被中断； false：未被中断
        virtual bool IsDocumentOpeningInterrupted() const = 0;

        /// \brief 设置链接数据库的元素过滤器
        /// 
        /// 用于加载数据时确定哪些Element被加载到链接文档，仅用于标准数据
        /// \param opElementFilter 元素过滤器对象
        /// \return 返回true表示设置成功，false表示返回失败
        virtual bool SetLinkDatasetElementFilter(OwnerPtr<ILinkDatasetElementFilter> opElementFilter) = 0;

        /// \brief 获取当前链接数据库的元素过滤器
        /// \return 元素过滤器
        virtual const ILinkDatasetElementFilter* GetLinkDatasetElementFilter() const= 0;   

        /// \brief 打开文档
        /// \param filePath 要打开的文档绝对路径 
        /// \param docLoadOptions 文件加载标记选项
        /// \param errorCode 输出参数，打开文档失败时，可以通过错误码判断错误类型
        /// \param pDocOpenValidator 文档打开验证器，不用的话，可以设置为空
        /// \param pUnrecognizedAssemblies 文档打开未识别的模块名称，作为输入返回值，不用的话，可以不设置，默认为空
        /// \return 返回Document对象，如果失败则返回空。
        GCMP_OBSOLETE_API(2021/05/01, IDocumentManager::OpenDocument(const std::wstring&, const DocumentLoadOptions&, DocumentLoadResult&, IDocumentOpenValidator*, std::set<std::wstring>*)) virtual IDocument* OpenDocument
        (
            const std::wstring& filePath,
            DocumentLoadOptions& docLoadOptions,
            DocumentLoadError& errorCode,
            IDocumentOpenValidator* pDocumentOpenValidator,
            std::set<std::wstring>* pUnrecognizedAssemblies
        ) = 0;

        /// \brief 开启Doc异步析构
        /// \return void
        virtual void EnableDocAsyncDestruct() = 0;
        /// \brief 清理异步析构线程
        /// \return void
        virtual void CleanDocAsyncDestructThread() = 0;
    };
}
